package cn.cyb.server.config;


import cn.cyb.server.security.filter.JwtAuthenticationTokenFilter;
import cn.cyb.server.security.handler.AuthenticationEntryPointImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.savedrequest.NullRequestCache;

/**
 * https://blog.csdn.net/weixin_46763762/article/details/144543486
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    // 认证失败处理
    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 如何注册第三方Bean对象
     * Bean对象注册，对象默认名字为方法名
     * 如果方法内部需要使用ioc容器中已存在对象，那么只需要在方法上声明即可
     * @return
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
//        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
//        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
//        daoAuthenticationProvider.setPasswordEncoder(new PasswordEncoder() {
//            @Override
//            public String encode(CharSequence rawPassword) {
//                logger.info("rawPassword=" + rawPassword);
//                return MD5.md5((String) rawPassword);
//            }
//
//            @Override
//            public boolean matches(CharSequence rawPassword, String encodedPassword) {
//                logger.info("rawPassword=" + rawPassword + " ,encodedPassword=" + encodedPassword);
//                return encodedPassword.equals(rawPassword);
//            }
//        });
//        return new ProviderManager(daoAuthenticationProvider);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置HttpSecurity
     *
     * @param httpSecurity
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        // 禁用用不到的功能，避免创建默认过滤器
        // 禁用csrf
        httpSecurity.csrf(csrf -> csrf.disable());
        // 禁用session
        httpSecurity.sessionManagement(AbstractHttpConfigurer::disable);
        // 无需给用户匿名身份
        httpSecurity.anonymous(AbstractHttpConfigurer::disable);
        httpSecurity.logout(AbstractHttpConfigurer::disable);
        // 表单登录禁用
        httpSecurity.formLogin(AbstractHttpConfigurer::disable);
        httpSecurity.httpBasic(AbstractHttpConfigurer::disable);
        // requestCache用于重定向，前后端分离项目无需重定向
        httpSecurity.requestCache(new Customizer<RequestCacheConfigurer<HttpSecurity>>() {
            @Override
            public void customize(RequestCacheConfigurer<HttpSecurity> httpSecurityRequestCacheConfigurer) {
                httpSecurityRequestCacheConfigurer.requestCache(new NullRequestCache());
            }
        });
        // 禁用HTTP响应标头
        httpSecurity.headers(headersConfigurer -> headersConfigurer.cacheControl(cacheControlConfig -> cacheControlConfig.disable()));
        httpSecurity.authorizeHttpRequests(new Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry>() {
            @Override
            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                registry.requestMatchers("/user/login", "/user/info", "/test2").permitAll();
                registry.requestMatchers("/monitor/server").permitAll();
                // 静态资源，可匿名访问
                // Swagger
                registry.requestMatchers("/swagger-ui/*", "/*/api-docs", "/*/api-docs/*").permitAll();
                // 除上面外的所有请求全部需要鉴权认证
                registry.anyRequest().authenticated();
            }
        });

        // 认证失败处理
        httpSecurity.exceptionHandling(new Customizer<ExceptionHandlingConfigurer<HttpSecurity>>() {
            @Override
            public void customize(ExceptionHandlingConfigurer<HttpSecurity> httpSecurityExceptionHandlingConfigurer) {
                httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(authenticationEntryPoint);
            }
        });
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        return httpSecurity.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return new WebSecurityCustomizer() {
            @Override
            public void customize(WebSecurity web) {
                // 配置一些请求不经过 httpSecurity 过滤器链
                web.ignoring().requestMatchers( "/static/**");
            }
        };
    }
}
